package io.bwqueen.nettylearn;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.util.concurrent.DefaultThreadFactory;

import java.net.InetSocketAddress;
import java.nio.channels.spi.SelectorProvider;

public class TestTCPNetty {

    public static void main(String[] args) {
        // 服务启动器
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        // ==================== 设置线程池
        // BOSS线程池
        // boss线程池实际上就是NIO框架中selector的工作角色，针对一个本地ip的端口，boss线程池中会有一条线程工作，工作内容也相对简单，
        // 就是发现新的连接；Netty是支持同时监听多个端口的，所以boss线程池的大小直接按照需要监听的服务器端口数量进行设置就行了
        EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);

        // work线程池
        DefaultThreadFactory threadFactory = new DefaultThreadFactory("work thread pool");
        // CPU个数
        int processorsNumber = Runtime.getRuntime().availableProcessors();
        // 这段代码主要是确定Netty中工作线程池的大小，这个大小一般是物理机器/虚拟机器 可用内核的个数 * 2。
        // work线程池中的线程都固定指派给了它所负责事件监听的网络连接，并根据事件状态，调用不同的ChannelHandler事件方法。
        // 而最后一个参数SelectorProvider说明了这个EventLoop所使用的多路复用IO模型为操作系统决定。
        NioEventLoopGroup workLoopGroup = new NioEventLoopGroup(processorsNumber * 2, threadFactory,
                SelectorProvider.provider());

        // 指定bossGroup和workGroup
        serverBootstrap.group(bossLoopGroup, workLoopGroup);
        // 如果是以下的声明方式，说明bossGroup和workGroup共享一个线程池
        // （实际上一般的环境下，这种共享线程池的方式已经够了）
        // serverBootstrap.group(workLoopGroup);

        // ==================== 设置服务的通道类型
        // 只能设置实现了ServerChannel接口的通道类
        serverBootstrap.channel(NioServerSocketChannel.class);
        // 还有另一种设置方法：
        //  serverBootstrap.channelFactory(new ChannelFactory<NioServerSocketChannel>() {
        //      public NioServerSocketChannel newChannel() {
        //          return new NioServerSocketChannel(SelectorProvider.provider());
        //      }
        //  })

        // ==================== 设置处理器
        // 此处展示一组简单的ByteArrayDecoder和ByteArrayEncoder
        // Netty的特色就在这一连串ChannelPipe中的处理器
        serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            protected void initChannel(NioSocketChannel ch) throws Exception {
                ch.pipeline().addLast(new ByteArrayEncoder());
                ch.pipeline().addLast(new TcpServerHandler());
                ch.pipeline().addLast(new ByteArrayDecoder());
            }
        });

        // ==================== 设置netty服务器绑定的ip和端口
        // ChannelOption.SO_BACKLOG的作用参考https://blog.csdn.net/weixin_40857858/article/details/121057700
        serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
        // ChannelOption.SO_KEEPALIVE当设置为true的时候，TCP会实现监控连接是否有效，当连接处于空闲状态的时候，超过了2个小时，
        // 本地的TCP实现会发送一个数据包给远程的socket，如果远程没有发回响应，TCP会持续尝试11分钟，直到响应为止，如果在12分钟的时候还没响应，
        // TCP尝试关闭socket连接。
        // 参考：
        // 1. https://blog.csdn.net/Qgwperfect/article/details/87967184
        // 2. https://www.cnblogs.com/acestart/p/14801869.html
        serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
        serverBootstrap.bind(new InetSocketAddress("0.0.0.0", 83));
        // 还可以监控多个端口
        // serverBootstrap.bind(new InetSocketAddress("0.0.0.0", 84);

    }
}
